﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace Green.Utility
{
    public static class ObjectExtensions
    {
        /// <summary>
        /// 类型转换
        /// </summary>
        /// <param name="value"></param>
        /// <param name="type"></param>
        /// <returns></returns>
        public static object ChangeType(this object value, Type type)
        {
            if (null == value)
            {
                return null;
            }
            if (type.IsGenericType)
            {
                if (type.GetGenericTypeDefinition() == typeof(Nullable<>))
                {
                    var o = type.GetGenericArguments()[0];
                    return ChangeType(value, o);
                }
                throw new InvalidCastException(string.Format("Invalid cast from \"{0}\" to \"{1}\".", value.GetType().FullName, type.FullName));
            }
            else if (type.IsEnum)
            {
                return Enum.Parse(type, value.ToString());
            }
            else if (type == typeof(Guid))
            {
                return new Guid(value.ToString());
            }
            else
            {
                return Convert.ChangeType(value, type);
            }
        }
        public static object ChangeType(this object value, Type type, object defaultValue)
        {
            var v = ChangeType(value, type);
            if (v == null)
                return defaultValue;
            return v.Equals(type.Default()) ? defaultValue : v;
        }
        /// <summary>
        /// 类型转换
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="value"></param>
        /// <returns></returns>
        public static T ChangeType<T>(this object value) where T : IConvertible
        {
            return (T)ChangeType(value, typeof(T));
        }
        public static T ChangeType<T>(this object value, T defaultValue) where T : IConvertible
        {
            var v = (T)ChangeType(value, typeof(T));
            if (v == null)
                return defaultValue;
            return v.Equals(default(T)) ? defaultValue : v;
        }

        /// <summary>
        /// 字符串是否为空
        /// </summary>
        /// <param name="str"></param>
        /// <param name="ignoreWhiteSpace">忽略空格？</param>
        /// <returns></returns>
        public static bool IsNullOrEmpty(this string str, bool ignoreWhiteSpace)
        {
            if (string.IsNullOrEmpty(str))
                return true;
            return ignoreWhiteSpace ? (str.Trim() == string.Empty) : false;
        }
        /// <summary>
        /// 字符串是否为空：默认忽略空格
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static bool IsNullOrEmpty(this string str)
        {
            return IsNullOrEmpty(str, true);
        }

        /// <summary>
        /// 集合是否为空
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <returns></returns>
        public static bool IsEmpty<T>(this IEnumerable<T> source)
        {
            return source != null && !source.Any();
        }

        public static object Default(this Type targetType)
        {
            return targetType.IsValueType ? Activator.CreateInstance(targetType) : null;
        }

        public static bool IsBetween<T>(this T t, T lowerBound, T upperBound, bool includeLowerBound, bool includeUpperBound)
        where T : IComparable<T>
        {
            if (t == null) throw new ArgumentNullException("t");

            var lowerCompareResult = t.CompareTo(lowerBound);
            var upperCompareResult = t.CompareTo(upperBound);

            return (includeLowerBound && lowerCompareResult == 0) ||
                (includeUpperBound && upperCompareResult == 0) ||
                (lowerCompareResult > 0 && upperCompareResult < 0);
        }

        public static bool IsBetween<T>(this T t, T lowerBound, T upperBound) where T : IComparable<T>
        {
            return IsBetween(t, lowerBound, upperBound, false, false);
        }

        /// <summary>
        /// 将键和值添加或替换到字典中：如果不存在，则添加；存在，则替换
        /// </summary>
        public static Dictionary<TKey, TValue> AddOrReplace<TKey, TValue>(this Dictionary<TKey, TValue> dict, TKey key, TValue value)
        {
            if (dict.ContainsKey(key))
                dict[key] = value;
            else
                dict.Add(key, value);
            return dict;
        }

        /// <summary>
        /// 获取与指定的键相关联的值，如果没有则返回输入的默认值
        /// </summary>
        public static TValue TryGetValue<TKey, TValue>(this Dictionary<TKey, TValue> dict, TKey key, TValue defaultValue)
        {
            return dict.ContainsKey(key) ? dict[key] : defaultValue;
        }

        /// <summary>
        /// 获取与指定的键相关联的值，如果没有则返回输入的默认值
        /// </summary>
        public static TValue TryGetValue<TKey, TValue>(this Dictionary<TKey, object> dict, TKey key, TValue defaultValue)where TValue:IConvertible
        {
            return dict.ContainsKey(key) ? dict[key].ChangeType<TValue>() : defaultValue;
        }


        /// <summary>
        /// 获取与指定的键相关联的值，如果没有则返回输入的默认值
        /// </summary>
        public static TValue TryGetValue<TKey, TValue>(this Dictionary<TKey, TValue> dict, TKey key)
        {
            return TryGetValue<TKey, TValue>(dict, key, default(TValue));
        }

        /// <summary>
        /// 向字典中批量添加键值对
        /// </summary>
        /// <param name="replaceExisted">如果已存在，是否替换</param>
        public static Dictionary<TKey, TValue> AddRange<TKey, TValue>(this Dictionary<TKey, TValue> dict, IEnumerable<KeyValuePair<TKey, TValue>> values, bool replaceExisted)
        {
            foreach (var item in values)
            {
                if (dict.ContainsKey(item.Key))
                    dict[item.Key] = item.Value;
                else dict.Add(item.Key, item.Value);
            }
            return dict;
        }

        /// <summary>
        /// 向字典中批量添加键值对,如果已存在
        /// </summary>        
        public static Dictionary<TKey, TValue> AddRange<TKey, TValue>(this Dictionary<TKey, TValue> dict, IEnumerable<KeyValuePair<TKey, TValue>> values)
        {
            return AddRange<TKey, TValue>(dict, values, true);
        }

        public static bool IsMatch(this string s, string pattern)
        {
            if (s == null) return false;
            return Regex.IsMatch(s, pattern);
        }

        public static string Match(this string s, string pattern)
        {
            if (s == null) return string.Empty;
            return Regex.Match(s, pattern).Value;
        }

        public static string ToHex(this byte b)
        {
            return b.ToString("X2");
        }

        public static string ToHex(this IEnumerable<byte> bytes)
        {
            var sb = new StringBuilder();
            foreach (byte b in bytes)
                sb.Append(b.ToString("X2"));
            return sb.ToString();
        }

        public static string ToBase64String(byte[] bytes)
        {
            return Convert.ToBase64String(bytes);
        }

        public static IEnumerable<T> Distinct<T, V>(this IEnumerable<T> source, Func<T, V> keySelector)
        {
            return source.Distinct(new CommonEqualityComparer<T, V>(keySelector));
        }

        public static IEnumerable<T> Distinct<T, V>(this IEnumerable<T> source, Func<T, V> keySelector, IEqualityComparer<V> comparer)
        {
            return source.Distinct(new CommonEqualityComparer<T, V>(keySelector, comparer));
        }

    }

    public class CommonEqualityComparer<T, V> : IEqualityComparer<T>
    {
        private Func<T, V> keySelector;
        private IEqualityComparer<V> comparer;

        public CommonEqualityComparer(Func<T, V> keySelector, IEqualityComparer<V> comparer)
        {
            this.keySelector = keySelector;
            this.comparer = comparer;
        }

        public CommonEqualityComparer(Func<T, V> keySelector)
            : this(keySelector, EqualityComparer<V>.Default)
        { }

        public bool Equals(T x, T y)
        {
            return comparer.Equals(keySelector(x), keySelector(y));
        }

        public int GetHashCode(T obj)
        {
            return comparer.GetHashCode(keySelector(obj));
        }
    }
}
